/**
 *    Copyright 2011 Eric Brown-Munoz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.brownmunoz.companyjcr.data.jcr;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.jcr.LoginException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.brownmunoz.companyjcr.data.CompanyDAO;
import com.brownmunoz.companyjcr.data.DataException;
import com.brownmunoz.companyjcr.model.Company;


/**
 * @author eric
 *
 *
 * 
 *    Copyright 2011 Eric Brown-Munoz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

public class CompanyDAOJCRImpl implements CompanyDAO {
	
	/**
	 * 
	 */
	private static final String COMPANIES = "companies";
	
	// Injected
	private JackRabbitHelper jackrabbit;
	
	private Logger logger = LoggerFactory.getLogger(CompanyDAOJCRImpl.class);
	
	/*
	 * Intended only for non Spring testing.
	 */
	protected CompanyDAOJCRImpl(JackRabbitHelper jackrabbit) {
		this.jackrabbit = jackrabbit;
	}
	
	/**
	 * Default constructor
	 */
	public CompanyDAOJCRImpl() { }

	
	/* (non-Javadoc)
	 * 
	 * @see com.brownmunoz.companyjcr.jcr.CompanyDAO#createCompanyNode(com.brownmunoz.companyjcr.model.Company)
	 * @returns the id (node name) of the created company.
	 */
	public CompanyJCRPersisted createCompanyNode (Company company) throws DataException {
		Session session = null;
		String nodeName = null;
		long uniqueNewCompanyID = jackrabbit.getUniqueID("companies");
		
		try {
			session = jackrabbit.openSession();
			Node root = session.getRootNode();		
			Node companiesNode;
			Node companyNode;

			if (root.hasNode(COMPANIES)) {
				companiesNode = root.getNode(COMPANIES);
				
			} else {
				companiesNode = root.addNode(COMPANIES);
				session.save();
			}
			
			nodeName = "company#" + String.valueOf(uniqueNewCompanyID);

			// TODO Error checking and uniqueness checking here
			companyNode = companiesNode.addNode(nodeName);
			
			writeCompanyToNode(companyNode, company, Calendar.getInstance());
			

			session.save();
		} catch (RepositoryException re) {
			throw new DataException("Error creating company node.", re);
		} finally {
			session.logout();
		}
		
		return new CompanyJCRPersisted(nodeName, company);
	}
	
	/**
	 * Update an existing company
	 * 
	 * @param company
	 * @throws DataException
	 * @throws RepositoryException
	 */
	public void updateCompanyNode (Company company) throws DataException, RepositoryException {
		if (! company.isPersisted()) {
			throw new DataException("Company object is not persisted");
		}
		
		Session session = null;
		
		try {
			session = jackrabbit.openSession();
			Node root = session.getRootNode();	
			
			Node companyNode = root.getNode(COMPANIES + "/" + company.getId());			
			writeCompanyToNode(companyNode, company, null);
			
			session.save();
			
		} finally {
			if (session != null) {
				session.logout();
			}
		}
		
	}	

	/**
	 * Get a company with id
	 * @param id the company id (i.e. the path name)
	 * @return The company object with that id
	 * @throws DataException if company doesn't exist
	 * @throws RepositoryException For repository problem.
	 */
	public Company getCompany(String id) throws DataException, RepositoryException {
		Session session = null;
		Company company;
		
		String nodeName = COMPANIES + "/" + id;
		
		try {
			session = jackrabbit.openSession();
		
			Node companyNode = session.getNode("/" + nodeName);
			
			company = getCompanyForNode(companyNode);
			
		
		} catch (PathNotFoundException e) {
			throw new DataException("Get Company: " + id + " does not exist", e);
		} catch (RepositoryException e) {
			logger.error("RepositoryException getting companies", e);
			throw e;
		} finally {
			if (session != null) {
				session.logout();
			}
		}		
		return company;
	}
	
	/* (non-Javadoc)
	 * @see com.brownmunoz.companyjcr.data.CompanyDAO#getCompanies()
	 */
	public List<Company> getCompanies() throws DataException {
		
		List <Company> retval = new ArrayList<Company>();
		
		Session session = null;
		
		try {
			session = jackrabbit.openSession();
		
			Node companyNode = session.getNode("/" + COMPANIES);
			NodeIterator nodeIterator = companyNode.getNodes();
		
			while (nodeIterator.hasNext()) {
				Node each = nodeIterator.nextNode();
				
				CompanyJCRPersisted eachCompany = getCompanyForNode(each);
				retval.add(eachCompany);				
			}
		
		} catch (RepositoryException e) {
			logger.error("RepositoryException getting companies", e);
			throw new DataException(e);
		} catch (DataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (session != null) {
				session.logout();
			}
		}
		
		return retval;
	}

	/*
	 * Write information from Company object to given node
	 */
	private void writeCompanyToNode (Node companyNode, Company company, Calendar created) throws RepositoryException {
		companyNode.setProperty("name", company.getName());
		companyNode.setProperty("description", company.getDescription());
		companyNode.setProperty("website", company.getWebsite());
		
		if (created != null) {
			companyNode.setProperty("created", created);
		}
	}
	
	/*
	 * Read information from Node to Company Object 
	 */
	private CompanyJCRPersisted getCompanyForNode(Node node) throws RepositoryException, DataException {
		
		String nodeName = node.getName();
		Company aCompany = new Company();
		
		PropertyIterator iterator = node.getProperties();
		while (iterator.hasNext()) {
			Property each = iterator.nextProperty();
			String name = each.getName();
			
			if (name == null) {
				continue;
			} else if (name.equals("name")) {
				aCompany.setName(each.getString());
			} else if (name.equals("description")) {
				aCompany.setDescription(each.getString());
			} else if (name.equals("website")) {
				aCompany.setWebsite(each.getString());
			} else if (name.equals("created")) {
				aCompany.setCreated(each.getDate());
			} else if (!name.startsWith("jcr:")){ // FIXME - I suspect there is a better way to check
				logger.error("Unknown poperty name for Node " + node + " name " + name);
			}
		}
		
		CompanyJCRPersisted retval = new CompanyJCRPersisted(nodeName, aCompany);
			return retval;
		}

	
	/**
	 * Injected
	 * 
	 * @param jackrabbit
	 */
	public void setJackrabbit(JackRabbitHelper jackrabbit) {
		this.jackrabbit = jackrabbit;
	}

	/* (non-Javadoc)
	 * @see com.brownmunoz.companyjcr.data.CompanyDAO#deleteCompanyNode(java.lang.String)
	 */
	public void deleteCompanyNode(String id) throws RepositoryException {
		Session session = null;
		
		try {
			session = jackrabbit.openSession();
		
			Node companyNode = session.getNode("/" + COMPANIES);
			Node toRemove = companyNode.getNode(id);
			if (toRemove != null) {
				toRemove.remove();
				session.save();
			}			
		} finally {
			session.logout();
		}
		
	}

}
